package org.yun.octopus.mongo.core;

import com.mongodb.client.result.UpdateResult;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Component;
import org.yun.octopus.base.config.Cfg;
import org.yun.octopus.base.spi.IMarkCache;
import org.yun.octopus.base.util.SystemTimer;
import org.yun.octopus.mongo.async.MongoAsyncMark;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import static org.yun.octopus.base.core.RollbackFactory.RELIABLE_FLAGS;
import static org.yun.octopus.base.core.RollbackFactory.UNRELIABLE_FLAGS;

/**
 * @author liyunfeng31
 */
@Component
public class MongoSyncMark implements IMarkCache {

    @Resource
    private MongoTemplate template;

    @Override
    public boolean syncSave(String group, String bizNo) {
        return  save(group,bizNo,0,0,null);
    }

    /**
     * 缓存打标
     *
     * @param group  业务tag
     * @param bizNo  业务编号
     * @param stepNo 步骤编号
     * @param flag   步骤结果标记
     */
    @Override
    public boolean save(String group, String bizNo, int stepNo, int flag) {
        return save(group,bizNo,stepNo,flag,null);
    }

    /**
     * 缓存打标
     *
     * @param group       业务tag
     * @param bizNo       业务编号
     * @param stepNo      步骤编号
     * @param code        步骤结果标记
     * @param childNo 子步骤编号
     */
    @Override
    public boolean save(String group, String bizNo, int stepNo, int code, String childNo) {

        if(Cfg.markAsync && stepNo != 0){
            return MongoAsyncMark.offer(group, bizNo, stepNo, code,childNo);
        }

        Query query = new Query();
        query.addCriteria(Criteria.where("bizNo").is(bizNo));
        Update update = Update.update("stepNo"+stepNo, code)
                .set("time", SystemTimer.currentTime())
                .set("group",group);
        if(childNo != null){
            update.set(stepNo+"No",childNo);
        }
        UpdateResult upsert = template.upsert(query, update, "mark");
        return upsert.getModifiedCount() == 1;
    }

    @Override
    public List<Integer> stepNos(String group, String bizNo) {
        Query query = new Query(Criteria.where("bizNo").is(bizNo));
        Map<String, Object> mark = template.findOne(query, Map.class, "mark");
        if(mark == null){ return null;}
        return mark.entrySet().stream()
                .filter(map -> match(map.getKey(), map.getValue()))
                .map(map->Integer.parseInt(map.getKey().replace("stepNo","")))
                .collect(Collectors.toList());
    }

    @Override
    public boolean clearMark(String group, String bizNo) {
        Query query = Query.query(Criteria.where("bizNo").is(bizNo));
        template.remove(query);
        return true;
    }

    @Override
    public List<Object> abnormalNos(String group, long st, long et, int limit) {
        Query query = new Query(Criteria.where("group").is("trade"))
                .addCriteria(Criteria.where("time").gt(st))
                .addCriteria(Criteria.where("time").lt(et)).limit(limit);
        List<Map> list = template.find(query, Map.class, "mark");
        List<Object> reList = new ArrayList<>();
        for (Map map : list) {
            reList.add(map.get("bizNo"));
        }
        return reList;
    }

    @Override
    public String childBizNo(String group, String parentBizNo, int no) {
        Query query = new Query(Criteria.where("bizNo").is(parentBizNo));
        Map<String, Object> mark = template.findOne(query, Map.class, "mark");
        if(mark == null){ return null;}
        return mark.get(no+"No").toString();
    }

    private boolean match(String key, Object val){
        Set<String> set  = Cfg.markAsync ? UNRELIABLE_FLAGS : RELIABLE_FLAGS;
        return key.startsWith("step") && set.contains(val.toString());
    }

}
